package netective.api.gatherer;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

import netective.Main;
import netective.api.analyzer.ByteConverter;
import netective.dao.DatabaseHelper;
import jpcap.JpcapCaptor;
import jpcap.NetworkInterface;
import jpcap.NetworkInterfaceAddress;

/**
 * Manager to getting devices and handling packets.
 * @author bartek
 * TODO:
 * multi captors open
 */
public class Captor {

    private DeviceInterface[] devices;
    private JpcapCaptor captor;
    private static boolean	deviceChanged;
    private int activedevice;

    public Captor() {
    	NetworkInterface[] netInterfaces = JpcapCaptor.getDeviceList();
    	devices = new DeviceInterface[netInterfaces.length];
        for (int i = 0; i < devices.length; i++) {
        	devices[i] = new DeviceInterface(netInterfaces[i], false);
        }
        captor = null;
        activedevice=-1;
        deviceChanged = false;
    }
    
    public boolean hasDeviceChanged()
    {
    	return deviceChanged;
    }
    
    public void setDeviceChanged(boolean hasChanged)
    {
    	deviceChanged = hasChanged;
    }

    public void setDevices(DeviceInterface[] devices) {
        this.devices = devices;
    }

    public void setCaptor(JpcapCaptor captor) {
        this.captor = captor;
    }

    public DeviceInterface[] getDevices() {
        return devices;
    }

    public JpcapCaptor getCaptor() {
        return captor;
    }

    public void handlePackets() {
    	PacketPrinter pp = new PacketPrinter(this);
    	pp.start();
    }

    public void closeCaptor() {
        captor.close();
    }

    /**
     * Opens device with specified index in devices array
     * @param index Index og device in devices array
     * @param snaplen Max number of bytes to capture at once
     * @param promics True if you want to open the interface in promiscuous mode, and otherwise false.
    In promiscuous mode, you can capture packets every packet from the wire, i.e., even if its source or destination MAC address is not same as the MAC address of the interface you are opening.
    In non-promiscuous mode, you can only capture packets send and received by your host.
     * @param to_ms Set a capture timeout value in milliseconds.
     */
    public boolean openDevice(int index, int snaplen, boolean promics, int to_ms) 
    {
        try 
        {
        	captor = JpcapCaptor.openDevice(devices[index], snaplen, promics, to_ms);
            devices[index].setState(true);
            System.out.println("\n\nCaptor opening: " + devices[index].description);
            deviceChanged = true;
            activedevice=index;
            
            try 
            {
				Thread.sleep(100);
			} 
            catch (InterruptedException e) 
            {
				e.printStackTrace();
			}
            
            String macAddress = ByteConverter.fromByteToHexString(getDevices()[getActivedevice()].mac_address);
            String description = getDevices()[getActivedevice()].description;
            DatabaseHelper dbHelper = new DatabaseHelper();
            int deviceFK = dbHelper.getDeviceFK(macAddress);
            if(deviceFK == -1)
            {
            	dbHelper.addNewDevice(macAddress, description);
            	deviceFK = dbHelper.getDeviceFK(macAddress);
            }
            DatabaseHelper.setActiveDeviceFK(deviceFK);
            
            handlePackets();
            
            return true;
        } 
        catch (IOException ex) 
        {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
        return false;

    }

    /**
     * Prints informations about all network devices in computer
     */
    public void printDevicesDetails() {
        System.out.println("Devices count: " + devices.length);

        for (int i = 0; i < devices.length; i++) {
            //print out its name and description
            System.out.println(i + ": " + devices[i].name + "(" + devices[i].description + ")");
            
            //print out its datalink name and description
            System.out.println(" datalink: " + devices[i].datalink_name + "(" + devices[i].datalink_description + ")");

            //print out its MAC address
            System.out.print(" MAC address:");
            for (byte b : devices[i].mac_address) {
                System.out.print(Integer.toHexString(b & 0xff) + ":");
            }
            //print out its IP address, subnet mask and broadcast address
            System.out.println("\nIP address: ");
            for (NetworkInterfaceAddress a : devices[i].addresses) {
                System.out.println(" address:" + a.address + " " + a.subnet + " " + a.broadcast);
            }
        }
    }

	public int getActivedevice() {
		return activedevice;
	}

}
